package bean

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"sync"
)

// 依赖注入tag名
var injectTagName = "bean"

// 工厂实例对象
type factory = func() (interface{}, error)

// 初始化依赖管理容器
func NewBeanContainer() *BeanContainer {
	return &BeanContainer{
		singletons: make(map[string]interface{}),
		transients: make(map[string]factory),
	}
}

// 依赖管理容器
type BeanContainer struct {
	sync.Mutex
	singletons map[string]interface{}
	transients map[string]factory
}

// 注册单例对象
func (bean *BeanContainer) SetSingleton(name string, singleton interface{}) {
	bean.Lock()
	bean.singletons[name] = singleton
	bean.Unlock()
}

// 读取单例对象
func (bean *BeanContainer) GetSingleton(name string) interface{} {
	return bean.singletons[name]
}

// 注册瞬时实例创建工厂方法
func (bean *BeanContainer) SetTransient(name string, factory factory) {
	bean.Lock()
	bean.transients[name] = factory
	bean.Unlock()
}

// 读取工厂实例对象
func (bean *BeanContainer) GetTransient(name string) interface{} {
	factory := bean.transients[name]
	instance, _ := factory()
	return instance
}

// Bean依赖注入
func (bean *BeanContainer) Entry(instance interface{}) error {
	err := bean.entryValue(reflect.ValueOf(instance))
	if err != nil {
		return err
	}
	return nil
}

func (bean *BeanContainer) entryValue(value reflect.Value) error {

	if value.Kind() != reflect.Ptr {
		return errors.New("the injected dependency must be a pointer")
	}

	elemType, elemValue := value.Type().Elem(), value.Elem()
	for i := 0; i < elemType.NumField(); i++ {
		if !elemValue.Field(i).CanSet() {
			// Cannot be set to skip
			continue
		}

		fieldType := elemType.Field(i)

		if fieldType.Anonymous {
			item := reflect.New(elemValue.Field(i).Type())
			// 递归注入依赖
			bean.entryValue(item)
			elemValue.Field(i).Set(item.Elem())
		} else {
			if elemValue.Field(i).IsZero() {
				// 零值才可以被注入
				tag := fieldType.Tag.Get(injectTagName)
				injectInstance, err := bean.getInstance(tag)
				if err != nil {
					return err
				}

				bean.entryValue(reflect.ValueOf(injectInstance))

				elemValue.Field(i).Set(reflect.ValueOf(injectInstance))
			}
		}
	}
	return nil
}

func (bean *BeanContainer) getInstance(tag string) (interface{}, error) {
	var injectName string

	tags := strings.Split(tag, ",")
	if len(tags) == 0 {
		injectName = ""
	} else {
		injectName = tags[0]
	}

	if bean.isTransient(tag) {
		factory, ok := bean.transients[injectName]
		if !ok {
			return nil, errors.New("transient factory not found")
		} else {
			return factory()
		}
	} else { //默认单例
		instance, ok := bean.singletons[injectName]
		if !ok || instance == nil {
			return nil, errors.New(injectName + " dependency not found")
		} else {
			return instance, nil
		}
	}
}

// 瞬时实例,每次使用都创建新的实例
func (bean *BeanContainer) isTransient(tag string) bool {
	tags := strings.Split(tag, ",")
	for _, name := range tags {
		if name == "transient" {
			return true
		}
	}
	return false
}

// 输入注入结果
func (bean *BeanContainer) String() string {
	lines := make([]string, 0, len(bean.singletons)+len(bean.transients)+2)
	fmt.Print(lines)
	lines = append(lines, "singletons:")
	for key, value := range bean.singletons {
		line := fmt.Sprintf("    %s: %x %s", key, bean.singletons[key], reflect.TypeOf(value).String())
		lines = append(lines, line)
	}

	lines = append(lines, "transients:")
	for key, value := range bean.transients {
		line := fmt.Sprintf("%s: %x %s", key, bean.transients[key], reflect.TypeOf(value).String())
		lines = append(lines, line)
	}
	return strings.Join(lines, "")
}

type IBeanContainer interface {
	// 注册单例对象
	SetSingleton(name string, singleton interface{})
	// 读取单例对象
	GetSingleton(name string) interface{}
	// 注册瞬时实例创建工厂方法
	SetTransient(name string, factory factory)
	// 读取工厂实例对象
	GetTransient(name string) interface{}
	// Bean依赖注入
	Entry(instance interface{}) error
	// 注入执行流程
	entryValue(value reflect.Value) error
	// 读取需要注入的实例
	getInstance(tag string) (interface{}, error)
	// 瞬时实例,每次使用都创建新的实例
	isTransient(tag string) bool
	// 输入注入结果
	String() string
}
